<template>
    <div class="container">
        <div ref="canvasContainer" class="canvas-container"></div>
        <!-- 左侧工具栏 -->
        <LeftToolbar :type="type" :selectedNode="selectedNode" @save="handleSave" :nodeList="nodeList" v-model:name="name" v-model:show="show" :leftToolbarList="leftToolbarList" @back="back" @dragStart="handleToolbarDragStart" @dragEnd="handleToolbarDragEnd" @nodeClick="nodeClick" />

        <!-- 属性配置 -->
        <div class="right" v-if="selectedNode && rightShow">
            <div class="right-tabs">
                <div v-if="bottomToolbarRef.bottomToolbarActive != 0 && bottomToolbarRef.bottomToolbarActive != 1 && bottomToolbarRef.bottomToolbarActive != 5" :class="rightTabs === 0?'right-tabs-active':'right-tabs-item' " @click="rightTabs = 0">配置
                </div>
                <div v-if="bottomToolbarRef.bottomToolbarActive!=4" :class="rightTabs === 1?'right-tabs-active':'right-tabs-item' " @click="rightTabs = 1">基本信息</div>
            </div>
            <div class="divider"></div>
            <div class="right-info" v-if="rightTabs === 1 && bottomToolbarRef.bottomToolbarActive !=4 &&selectedNode.label ">
                <div class="right-info-name">{{ selectedNode.label }}</div>
                <div class="right-info-item">
                    <div class="right-info-item-label">厂家</div>
                    <div class="right-info-item-value">{{ selectedNode.manufactor }}</div>
                </div>
                <div class="right-info-item">
                    <div class="right-info-item-label">型号</div>
                    <div class="right-info-item-value">{{ selectedNode.model }}</div>
                </div>
                <div class="right-info-item">
                    <div class="right-info-item-label">作用</div>
                    <div class="right-info-item-value">{{ selectedNode.remark }}</div>
                </div>
                <div class="right-info-item">
                    <div class="right-info-item-label">前节点</div>
                    <div class="right-info-item-value"> <el-tag v-for="type in selectedNode.firstNode" :key="type.id" type="primary">{{ getTypeName(type.nodeEquipmentType) }}</el-tag></div>
                </div>
                <div class="right-info-item">
                    <div class="right-info-item-label">后节点</div>
                    <div class="right-info-item-value"> <el-tag v-for="type in selectedNode.afterNode" :key="type.id" type="primary">{{ getTypeName(type.nodeEquipmentType) }}</el-tag></div>
                </div>
                <el-button v-if="bottomToolbarRef.bottomToolbarActive ==5" class="right-config-del" type="primary" @click="delModel">删除模型</el-button>

            </div>

            <div class="right-config" v-else>
                <div class="right-config-line" v-if="selectedNode.line && bottomToolbarRef.bottomToolbarActive !=5">
                    <div class="right-config-line-error" v-if="selectedNode.errorMsg">{{ selectedNode.errorMsg }}</div>
                    <div class="right-config-line-node">
                        <div class="right-config-line-node-label">前节点</div>
                        <el-select placeholder="请选择前节点" :disabled="type==1" v-model="lineStartId" @change="handleLineNodeChange(1)">
                            <template v-for="node in nodeList" :key="node.id">
                                <el-option v-if="node.id != lineEndId" :label="node.label" :value="node.id" />
                            </template>
                        </el-select>
                    </div>
                    <div class="right-config-line-node">
                        <div class="right-config-line-node-label">后节点</div>
                        <el-select placeholder="请选择后节点" v-model="lineEndId" :disabled="type==1" @change="handleLineNodeChange(2)">
                            <template v-for="node in nodeList" :key="node.id">
                                <el-option v-if="node.id != lineStartId" :label="node.label" :value="node.id" />
                            </template>

                        </el-select>
                    </div>
                    <el-button v-if="type==0" class="right-config-del" type="primary" @click="delLine">删除线</el-button>
                </div>
                <div class="right-config-scale" v-if="bottomToolbarRef.bottomToolbarActive ==2">
                    <div class="right-config-scale-label"> 等比缩放</div>
                    <InputNumber :value="selectedNode.meshRef.scale.x" @change="handleScaleChange" />
                    <el-button class="right-config-scale-btn" type="primary" @click="handleToolbarClick(0)">退出等比缩放模式</el-button>
                </div>
                <div class="right-config-rotation" v-if="bottomToolbarRef.bottomToolbarActive ==3">
                    <div class="right-config-rotation-item">
                        <div class="right-config-rotation-item-label"> X轴旋转角度</div>
                        <InputNumber :icType="1" :value="selectedNode.meshRef.rotation.x" @change="(e)=>{handleRotationChange(e,1)}" />
                    </div>
                    <div class="right-config-rotation-item">
                        <div class="right-config-rotation-item-label"> Y轴旋转角度</div>
                        <InputNumber :icType="1" :value="selectedNode.meshRef.rotation.y" @change="(e)=>{handleRotationChange(e,2)}" />
                    </div>
                    <div class="right-config-rotation-item">
                        <div class="right-config-rotation-item-label"> Z轴旋转角度</div>
                        <InputNumber :icType="1" :value="selectedNode.meshRef.rotation.z" @change="(e)=>{handleRotationChange(e,3)}" />
                    </div>
                    <el-button class="right-config-rotation-btn" type="primary" @click="handleToolbarClick(0)">退出旋转参数配置</el-button>
                </div>

                <div class="right-config-rotation" v-if="bottomToolbarRef.bottomToolbarActive ==6">
                    <div class="right-config-rotation-item">
                        <div class="right-config-rotation-item-label" style="color:#ff7500,"> X轴坐标</div>
                        <InputNumber :min="-100" :max="100" :icType="1" :value="selectedNode.meshRef.position.x" @change="(e)=>{handlePositionChange(e,1)}" />
                    </div>
                    <div class="right-config-rotation-item">
                        <div class="right-config-rotation-item-label" style="color:#0f0"> Y轴坐标</div>
                        <InputNumber :min="-100" :max="100" :icType="1" :value="selectedNode.meshRef.position.y" @change="(e)=>{handlePositionChange(e,2)}" />
                    </div>
                    <div class="right-config-rotation-item">
                        <div class="right-config-rotation-item-label" style="color:#00f"> Z轴坐标</div>
                        <InputNumber :min="-100" :max="100" :icType="1" :value="selectedNode.meshRef.position.z" @change="(e)=>{handlePositionChange(e,3)}" />
                    </div>
                    <el-button class="right-config-rotation-btn" type="primary" @click="handleToolbarClick(0)">退出坐标参数配置</el-button>
                </div>
                <!-- <el-button v-if="bottomToolbarActive ==5" class="right-config-del" type="primary" @click="delModel">删除模型</el-button> -->
            </div>

        </div>
        <div class="right-pack" v-if="selectedNode" @click="rightShow = !rightShow">
            <img src="@/assets/images/pack.png" />
        </div>
        <!-- 底部工具栏 -->
         <BottomToolbar ref="bottomToolbarRef" v-model:rightTabs="rightTabs" v-model:controls="controls" :selectedNode="selectedNode"/>
        <!-- <div class="bottom" v-if="type==0">
            <div :class="bottomToolbarActive==0?'bottom-actvie':'bottom-item'" @click="handleToolbarClick(0)">
                <img src="@/assets/images/toolbar-arrow.png" />
            </div>
            <div :class="bottomToolbarActive==1?'bottom-actvie':'bottom-item'" @click="handleToolbarClick(1)">
                <el-tooltip effect="light" content="场景缩放旋转" placement="top"> <img src="@/assets/images/toolbar-move.png" /></el-tooltip>
            </div>
            <div :class="bottomToolbarActive==2?'bottom-actvie':'bottom-item'" @click="handleToolbarClick(2)">
                <el-tooltip effect="light" content="模型大小缩放" placement="top"> <img src="@/assets/images/toolbar-scale.png" /></el-tooltip>
            </div>
            <div :class="bottomToolbarActive==3?'bottom-actvie':'bottom-item'" @click="handleToolbarClick(3)">
                <el-tooltip effect="light" content="模型xyz旋转" placement="top"> <img src="@/assets/images/toolbar-rotate.png" /></el-tooltip>
            </div>
            <div :class="bottomToolbarActive==6?'bottom-actvie':'bottom-item'" @click="handleToolbarClick(6)">
                <el-tooltip effect="light" content="模型xyz位置" placement="top"> <img src="@/assets/images/toolbar-position.png" /></el-tooltip>
            </div>
            <div draggable="true" @dragstart="handleToolbarDragStart({ type: 'line1'}, $event)" @dragend="handleToolbarDragEnd" :class="bottomToolbarActive==4?'bottom-actvie':'bottom-item'" @click="handleToolbarClick(4)">
                <el-tooltip effect="light" content="拖拽连接线" placement="top"> <img style="user-drag: none; -webkit-user-drag: none;" src="@/assets/images/toolbar-line.png" /></el-tooltip>
            </div>

            <div :class="bottomToolbarActive==5?'bottom-actvie':'bottom-item'" @click="handleToolbarClick(5)">
                <el-tooltip effect="light" content="删除节点模型" placement="top"> <img src="@/assets/images/toolbar-del.png" />
                </el-tooltip>
            </div>
        </div> -->
        <Topology ref="topologyRef" :nodeList="nodeList" :data="connectionsList" />
        <img class="coord" src="@/assets/images/coord.png" />

        <!-- <Topology :class=" show? 'slide-in-right' : 'slide-out-right' " ref="topologyRef" v-if="show && nodeList.length" :nodeList="nodeList" :data="connectionsList" /> -->
    </div>
</template>

<script setup name="Design">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { DragControls } from 'three/examples/jsm/controls/DragControls.js'
import { throttle } from 'lodash-es'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { getEquipmetVos, updateImg } from "@/api/simulation/design.js";
import { getCurrentInstance } from 'vue'
import InputNumber from './components/InputNumber.vue'
import LeftToolbar from './components/LeftToolbar.vue'
import BottomToolbar from './components/BottomToolbar.vue'
import Topology from './components/Topology.vue'
import { mergeGeometries, mergeVertices } from "three/addons/utils/BufferGeometryUtils.js";
import { saveModel, getModelById, updateMpdel } from '@/api/simulation/design'
import { EdgesGeometry, LineSegments, LineBasicMaterial } from 'three';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
const router = useRouter();
const { proxy } = getCurrentInstance()
const topologyRef = ref(null);//二维拓扑图组件引用
const canvasContainer = ref(null);
let scene, camera, renderer, controls, dragControls;// 场景元素
const nodeList = ref([])//场景中的所有模型
let connectionsList = [];//线集合
const pointList = [];//线的两端的圆点
const selectedNode = ref(null);// 选中节点模型
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const models = {};//保存模型实例
const leftToolbarList = ref([]);//左侧工具栏数据
const equipmentList = ref([]);//所有设备列表
const name = ref("未命名文件");
const rightTabs = ref(1);//右侧属性和信息切换
const bottomToolbarActive = ref(0);
const show = ref(true); //两边工具栏是否显示
const lineColor = 0x00e09e;//绿色
const lineErrorColor = 0xff0000;//红色
const selectedColor = 0x0066FF;//模型选中的盒子颜色
const lineStartId = ref(null);//当前选中的线段前节点
const lineEndId = ref(null);//后节点
const rightShow = ref(true);//右侧工具栏是否显示
const bottomToolbarRef = ref(null);
let formData = {};//详情数据
let labelRendererObj = {};//所有场景中模型标签渲染器
let id;//主键id
let boxHelper; //当前选中模型的包装盒对象
let currentDraggingNode = null;// 工具变量
const type = ref(0);//用来区分编辑页和详情页
//xyz位置
const handlePositionChange = (e, type) => {
    if (selectedNode.value && e) {
        switch (type) {
            case 1:
                selectedNode.value.meshRef.position.x = e;
                break;
            case 2:
                selectedNode.value.meshRef.position.y = e;
                break;
            case 3:
                selectedNode.value.meshRef.position.z = e;
                break;
        }
        updateConnections({ object: selectedNode.value.meshRef });
    }
}

// 处理缩放变化
const handleScaleChange = (e) => {
    if (selectedNode.value && e) {
        selectedNode.value.meshRef.scale.set(e, e, e);
        updateConnections({ object: selectedNode.value.meshRef }); // 更新连接线
    }
}
// 处理旋转x
const handleRotationChange = (e, type) => {
    if (selectedNode.value && e) {
        switch (type) {
            case 1:
                selectedNode.value.meshRef.rotation.x = e;
                break;
            case 2:
                selectedNode.value.meshRef.rotation.y = e;
                break;
            case 3:
                selectedNode.value.meshRef.rotation.z = e;
                break;
        }
        updateConnections({ object: selectedNode.value.meshRef });

    }
}
const highlightManager = {
    currentHighlight: null,
    edgeMaterial: new THREE.LineBasicMaterial({
        color: 0x00ff00,
        linewidth: 2,
        transparent: true,
        opacity: 0.8
    }),
    objectProxies: new WeakMap(), // 使用WeakMap自动内存管理

    // 核心修改：创建带监听器的对象代理
    createProxy(object) {
        if (this.objectProxies.has(object)) {
            return this.objectProxies.get(object)
        }

        const proxy = new Proxy(object, {
            set: (target, prop, value) => {
                const needsUpdate = ['position', 'rotation', 'scale'].includes(prop)
                if (needsUpdate) {
                    this.scheduleSync(target)
                }
                return Reflect.set(target, prop, value)
            }
        })

        this.objectProxies.set(object, proxy)
        return proxy
    },

    // 新增：批量更新调度（性能优化）
    scheduleSync(object) {
        if (!this.pendingSync) {
            this.pendingSync = new Set()
            requestAnimationFrame(() => {
                this.pendingSync.forEach(obj => this.syncPosition(obj))
                this.pendingSync.clear()
            })
        }
        this.pendingSync.add(object)
    },

    createHighlight(object) {
        if (object.userData?.edgeMesh) {
            this.syncPosition(object) // 确保已有高亮位置正确
            return object.userData.edgeMesh
        }

        const edgeThreshold = this.calculateEdgeThreshold(object)
        const edges = new THREE.EdgesGeometry(object.geometry, edgeThreshold)
        const edgeMesh = new THREE.LineSegments(edges, this.edgeMaterial.clone())

        // 配置边缘属性
        edgeMesh.renderOrder = 999
        edgeMesh.visible = true // 初始可见
        edgeMesh.userData = {
            isEdge: true,
            parentObject: null // 需要后续挂载到父对象
        }

        // 父对象关联（需先清理可能的旧引用）
        if (object.userData.edgeMesh) {
            object.remove(object.userData.edgeMesh)
        }

        object.add(edgeMesh)
        object.userData.edgeMesh = edgeMesh
        edgeMesh.userData.parentObject = object

        // 缓存矩阵更新
        object.updateMatrixWorld(true)
        edgeMesh.updateMatrixWorld(true)

        return edgeMesh
    },

    // 修改后的设置高亮方法
    setHighlight(rawObject) {
        this.clearHighlight()

        const object = this.createProxy(rawObject)
        const edgeMesh = this.createHighlight(object)

        edgeMesh.visible = true
        this.currentHighlight = { object, edgeMesh }

        // 初始同步
        this.syncPosition(object)
        return object
    },

    // 增强型清除方法
    clearHighlight() {
        if (!this.currentHighlight) return

        const { object, edgeMesh } = this.currentHighlight
        try {
            edgeMesh.visible = false
            object.remove(edgeMesh)
            edgeMesh.geometry.dispose()

            // 清理引用
            delete object.userData.edgeMesh
            this.objectProxies.delete(object)
        } catch (e) {
            console.error('Cleanup error:', e)
        }
        this.currentHighlight = null
    },

    // 精准同步方法
    syncPosition(object) {
        const edgeMesh = object.userData?.edgeMesh
        if (!edgeMesh) return

        try {
            // 通过世界矩阵直接计算
            const matrix = object.matrixWorld.clone()
            edgeMesh.position.setFromMatrixPosition(matrix)
            edgeMesh.rotation.setFromRotationMatrix(matrix)
            edgeMesh.scale.copy(object.scale)

            // 优化GPU更新频率
            if (edgeMesh.geometry.attributes.position) {
                edgeMesh.geometry.attributes.position.needsUpdate = true
            }
        } catch (error) {
            console.warn('Position sync failed:', error)
        }
    },

    // 动态阈值调整（优化多边形性能）
    calculateEdgeThreshold(object) {
        if (!object.geometry.boundingSphere) {
            object.geometry.computeBoundingSphere()
        }
        const sizeFactor = object.geometry.boundingSphere.radius * 0.5
        return THREE.MathUtils.clamp(sizeFactor, 15, 45)
    },


}
//线的节点更改事件
const handleLineNodeChange = (type) => {
    //1开始 2结束
    if (type == 1) {
        const node = nodeList.value.find(item => item.id == lineStartId.value);
        connectionsList.forEach(item => {
            if (item.id == selectedNode.value.id) {
                item.startId = node.id;
            }
        })
        updateConnections({ object: node.meshRef });
        proxy.$modal.msgSuccess("连接到" + node.label);
    } else {
        const node = nodeList.value.find(item => item.id == lineEndId.value);
        connectionsList.forEach(item => {
            if (item.id == selectedNode.value.id) {
                item.endId = node.id;
            }
        })
        updateConnections({ object: node.meshRef });
        proxy.$modal.msgSuccess("连接到" + node.label);
    }
    initDragControls();
    updateTopology();
}


//返回上一页
const back = async () => {
    try {
        if (type.value == 0) {
            await proxy.$modal.confirm('是否确认返回并保存');
            await handleSave();
        }
        router.go(-1);
    } catch (e) {
        router.go(-1);
    }
}

/**
 * 底部工具栏点击事件
 * @param e 
 */
const handleToolbarClick = (e) => {
    switch (e) {
        case 0:
            rightTabs.value = 1
            bottomToolbarActive.value = 0;
            if (type.value == 0) {
                controls.enableRotate = false; // 禁止旋转
                controls.enableZoom = false;   // 禁止缩放
            } else {
                controls.enableRotate = true; // 禁止旋转
                controls.enableZoom = true;   // 禁止缩放
            }
            break;
        case 1:
            //摄像机
            rightTabs.value = 1
            bottomToolbarActive.value = 1;
            controls.enableRotate = true; // 禁止旋转
            controls.enableZoom = true;   // 禁止缩放
            break;
        case 2:
            //缩放
            if (selectedNode.value?.meshRef) {
                rightTabs.value = 0
                bottomToolbarActive.value = 2;
                controls.enableRotate = false; // 禁止旋转
                controls.enableZoom = false;   // 禁止缩放
            }
            break;
        case 3:
            //旋转
            if (selectedNode.value?.meshRef) {
                rightTabs.value = 0
                bottomToolbarActive.value = 3;
                controls.enableRotate = false; // 禁止旋转
                controls.enableZoom = false;   // 禁止缩放
            }
            break;
        case 4:
            //线
            rightTabs.value = 0
            bottomToolbarActive.value = 4;
            if (type.value == 0) {
                controls.enableRotate = false; // 禁止旋转
                controls.enableZoom = false;   // 禁止缩放
            } else {
                controls.enableRotate = true; // 禁止旋转
                controls.enableZoom = true;   // 禁止缩放
            }
            resetHighlight();
            break;
        case 5:
            //删除
            rightTabs.value = 1
            bottomToolbarActive.value = 5;
            controls.enableRotate = false; // 禁止旋转
            controls.enableZoom = false;   // 禁止缩放
            break;
        case 6:
            //位置
            if (selectedNode.value?.meshRef) {
                rightTabs.value = 0
                bottomToolbarActive.value = 6;
                controls.enableRotate = false; // 禁止旋转
                controls.enableZoom = false;   // 禁止缩放
            }

            break;
    }
}

//id获取设备类型对应名称
const getTypeName = (id) => {
    return leftToolbarList.value.find(item => item.typeId == id)?.typeName;
}

const getCanvasBlob = () => {
    return new Promise((resolve) => {
        renderer.domElement.toBlob((blob) => {
            resolve(blob);
        }, 'image/png');
    });
}


/**
 * 保存
 */
const handleSave = async () => {
    try {
        if (!name.value) {
            proxy.$modal.msgError('名称不能为空');
            return;
        }
        proxy.$modal.loading('保存中')
        const formData = new FormData();
        const blob = await getCanvasBlob();
        formData.append('file', blob);
        const file = await updateImg(formData);

        //新增
        //线两端点  lineId 点关联的线id direction 方向 1起点 w 终点 
        let points = pointList.map(i => {
            return {
                id: i.id, lineId: i.lineId, direction: i.direction
                , position: [i.point.position.x, i.point.position.y, i.point.position.z],
            }
        })
        //startId 模型开始节点id endId 模型结束节点id  关联点的id  ePointId sPointId     status 0正常 1异常 线的颜色 
        let lines = connectionsList.map(i => {
            i.line.updateMatrixWorld(true)
            return {
                id: i.id, startId: i.startId, endId: i.endId, sPointId: i.sPointId, ePointId: i.ePointId
                , array: i.line.geometry.attributes.position.array, position: i.line.position, errorMsg: i.errorMsg
            }
        })
        //模型节点 nodeId设备id  label设备名称  manufactor厂家 typeId设备类型id
        let nodes = nodeList.value.map(i => {
            return {
                id: i.id, nodeId: i.nodeId, label: i.label, manufactor: i.manufactor,
                model: i.model, afterNode: i.afterNode, firstNode: i.firstNode, typeId: i.typeId,
                position: [i.meshRef.position.x, i.meshRef.position.y, i.meshRef.position.z],
                rotation: [i.meshRef.rotation.x, i.meshRef.rotation.y, i.meshRef.rotation.z],
                scale: [i.meshRef.scale.x, i.meshRef.scale.y, i.meshRef.scale.z],
            }
        })
        let modelContent = JSON.stringify({ points: points, lines: lines, nodes: nodes, camera: camera.position, pack: show.value });
        if (id) {
            let form = {
                id: id,
                modelName: name.value,

                modelContent: modelContent,
                modelUrl: file.fileName
            }
            //更新
            await updateMpdel(form);
        } else {
            let form = {
                modelName: name.value,
                modelContent: modelContent,
                modelUrl: file.fileName
            }
            const res = await saveModel(form);
            id = res.data.id
        }
        proxy.$modal.msgSuccess("保存成功");
    } catch (e) {
        proxy.$modal.msgError("保存失败");
    } finally {
        proxy.$modal.closeLoading();
    }
}

//场景数据
const initSceneData = () => {
    //线
    formData.modelContent.lines.forEach(i => {
        // 1. 创建顶点数据
        const positions = new Float32Array([i.array[0], i.array[1], i.array[2], i.array[3], i.array[4], i.array[5]]);
        const geometry = new THREE.BufferGeometry();
        geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        // 2. 修正材质参数
        const material = new THREE.LineBasicMaterial({
            color: lineColor,    // 确保高对比度颜色
            linewidth: 6,      // 线宽设置
            depthTest: false    // 禁用深度测试防止遮挡
        });
        const line = new THREE.Line(geometry, material);
        if (i.position) {
            line.position.set(i.position.x, i.position.y, i.position.z);
        } else {
            line.position.set(0, 0, 0);
        }

        line.geometry.attributes.position.needsUpdate = true
        line.geometry.computeBoundingSphere();
        // 3. 添加至场景
        scene.add(line);
        line.name = i.id;
        line.updateMatrixWorld(true);
        const startPoint = addControlPoint(new THREE.Vector3(i.array[0], i.array[1], i.array[2]));
        const endPoint = addControlPoint(new THREE.Vector3(i.array[3], i.array[4], i.array[5]));

        startPoint['userData']['type'] = 'point';
        endPoint['userData']['type'] = 'point';
        let sId = i.sPointId;
        let eId = i.ePointId
        endPoint.name = eId;
        startPoint.name = sId;
        //开始点
        pointList.push({
            id: sId,
            lineId: i.id,
            point: startPoint,
            direction: 1
        });
        //结束点
        pointList.push({
            id: eId,
            lineId: i.id,
            point: endPoint,
            direction: 2

        });
        connectionsList.push({
            id: i.id,
            line: line,
            start: null,
            end: null,
            startId: i.startId,
            endId: i.endId,
            sPoint: startPoint,
            ePoint: endPoint,
            sPointId: sId,
            ePointId: eId,
            errorMsg: i.errorMsg,
            status: i.status,//0 正常 1连接线逻辑错误
        });

    })
    // for (const l of connectionsList) {
    //     updateConnections({ object: l.line });
    // }
    // for (const l of pointList) {
    //     updateConnections({ object: l.point });
    // }
    if (connectionsList.length) {
        updateConnections({ object: connectionsList[0].line });
    }
    //节点
    formData.modelContent.nodes.forEach(i => {
        //加载模型
        let glb = models[i.nodeId].clone();
        //设置模型缩放大小
        if (i.scale) {
            glb.scale.set(...i.scale);
        }

        //设置模型旋转角度
        if (i.rotation) {
            glb.rotation.set(...i.rotation);
        }
        // const bbox = new THREE.Box3().setFromObject(glb);
        // const center = new THREE.Vector3();
        // bbox.getCenter(center)
        // glb.position.sub(center); // 将模型中心点对齐到原点
        glb.position.set(i.position[0], i.position[1], i.position[2]); // 设置到目标位置
        glb.userData['type'] = 'model';
        glb.userData['id'] = i.id;
        scene.add(glb)
        //给模型添加标签
        createAccurateLabel(glb, i.label, i.id);

        // 立即渲染
        const newNode = {
            id: i.id,//唯一标识
            nodeId: i.nodeId, //设备id
            label: i.label,//设备名称
            manufactor: i.manufactor,//厂家
            model: i.model,//型号
            meshRef: glb,//模型实例
            afterNode: i.afterNode,//下节点
            firstNode: i.firstNode,//上节点
            typeId: i.typeId //设备类型id
        }
        nodeList.value.push(newNode)

    })

    // for (const node of nodeList.value) {
    //     updateConnections({ object: node.meshRef });
    // }
    if (nodeList.value.length) {
        updateConnections({ object: nodeList.value[0].meshRef });
    }

    //设置相机位置
    if (formData.modelContent.camera) {
        camera.position.set(formData.modelContent.camera.x, formData.modelContent.camera.y, formData.modelContent.camera.z);
    }
    //两侧工具栏显示隐藏
    show.value = formData.modelContent.pack;
    updateTopology();
    initDragControls();
    requestAnimationFrame(() => {
        renderer.render(scene, camera);
    });
}


/**
 * 根据类型获取设备列表
 */
const getData = async () => {
    id = router.currentRoute.value.query.id;
    if (id) {
        const detialRes = await getModelById({ id: id });
        formData = detialRes.data;
        name.value = formData.modelName;
        if (formData.modelContent) {
            formData.modelContent = JSON.parse(formData.modelContent);
            console.log("🚀 ~ getData ~ formData.modelContent:", formData.modelContent);
        }
    }

    const res = await getEquipmetVos();
    //将所有设备遍历出来
    res.data.forEach(i => {
        i.imgPath = import.meta.env.VITE_APP_BASE_API + i.imgPath;
        i.equipmentManufactorVos.forEach(j => {
            j.equipments.forEach(k => {
                k.imgPath = import.meta.env.VITE_APP_BASE_API + k.imgVos[0].url;
                equipmentList.value.push(k);
            })
        })
    })
    leftToolbarList.value = res.data;
}
/**
 * 模型列表点击事件
 */
const nodeClick = (item) => {
    //点击模型 将工具栏切换为0
    if (bottomToolbarActive.value == 4) {
        handleToolbarClick(0);
        rightTabs.value = 1;
    }
    //highlight(item.meshRef, item.id);
    delBoxHelper();
    boxHelper = new THREE.BoxHelper(item.meshRef, selectedColor);
    scene.add(boxHelper);
    //  initDragControls();
    requestAnimationFrame(() => {
        renderer.render(scene, camera);
    });
    selectedNode.value = item;
}


/**
 * 创建线
 */
const createLine = (nodeConfig, position) => {
    // 2. 创建顶点数据
    const positions = new Float32Array([
        position.x, position.y, position.z,
        position.x + 2, position.y, position.z
    ]);
    const geometry = new THREE.BufferGeometry();
    geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    // 2. 修正材质参数
    const material = new THREE.LineBasicMaterial({
        color: lineColor,    // 确保高对比度颜色
        linewidth: 6,      // 线宽设置
        depthTest: false    // 禁用深度测试防止遮挡
    });

    const line = new THREE.Line(geometry, material);

    // 3. 强制添加至场景
    scene.add(line);
    let id = generateUniqueId();
    line.name = id;
    const start = new THREE.Vector3(0, 0, 0);
    const end = new THREE.Vector3(4, 0, 0);
    //线的两端开始结束圆点
    const startPoint = addControlPoint(new THREE.Vector3(position.x, position.y, position.z));
    const endPoint = addControlPoint(new THREE.Vector3(position.x + 2, position.y, position.z));
    startPoint['userData']['type'] = 'point';
    endPoint['userData']['type'] = 'point';
    let sId = generateUniqueId();
    let eId = generateUniqueId();
    endPoint.name = eId;
    startPoint.name = sId;
    //开始点
    pointList.push({
        id: sId,
        lineId: id,
        point: startPoint,
        direction: 1
    });
    //结束点
    pointList.push({
        id: eId,
        lineId: id,
        point: endPoint,
        direction: 2
    });
    connectionsList.push({
        id: id,
        line: line,
        start: null,
        end: null,
        startId: null,
        endId: null,
        sPoint: startPoint,
        ePoint: endPoint,
        sPointId: sId,
        ePointId: eId,
        status: 0,//0 正常 1连接线逻辑错误
    });
    initDragControls();
}


//删除模型和已经关联的线
const delModel = async () => {
    try {
        if (selectedNode.value) {
            await proxy.$modal.confirm(`是否确认删除${selectedNode.value.label}?`);
            for (let i = 0; i < nodeList.value.length; i++) {
                if (nodeList.value[i].id == selectedNode.value.id) {
                    //删除模型
                    if (nodeList.value[i].meshRef.parent) {
                        nodeList.value[i].meshRef.parent.remove(nodeList.value[i].meshRef);
                    } else {
                        scene.remove(nodeList.value[i].meshRef);
                    }
                    nodeList.value[i].meshRef.traverse((child) => {
                        if (child.isCSS2DObject) {
                            // 从 DOM 中移除元素
                            child.element.remove();
                            // 从 Three.js 场景中解除绑定
                            child.removeFromParent();
                        }
                    });
                    //删除当前选中的盒子
                    delBoxHelper();
                    //删除名称
                    delete labelRendererObj[nodeList.value[i].id];
                    //遍历线 将线关联的模型id赋值为空
                    connectionsList.forEach(line => {
                        if (line.startId == selectedNode.value.id) {
                            if (line.arrow) {
                                scene.remove(line.arrow)
                                line['arrow'] = null;
                            }
                            line.startId = null;
                        }
                        if (line.endId == selectedNode.value.id) {
                            if (line.arrow) {
                                scene.remove(line.arrow)
                                line['arrow'] = null;
                            }

                            line.endId = null;
                        }
                    })
                    nodeList.value[i].meshRef = null;
                    nodeList.value.splice(i, 1);
                    selectedNode.value = null
                    proxy.$modal.msgSuccess("删除成功");
                    //更新拖拽控制器
                    initDragControls();
                    break
                }
            }
            updateTopology();
        } else {
            proxy.$modal.msgError("请选择要删除的模型");
        }
    } catch (error) {
        console.log("🚀 ~ delModel ~ error:", error);
    }
}


//删除连接接线
const delLine = async () => {
    try {
        if (selectedNode.value) {
            await proxy.$modal.confirm('是否确认删除该线？');
            for (let i = 0; i < connectionsList.length; i++) {
                if (connectionsList[i].id == selectedNode.value.id) {
                    //删除箭头
                    if (connectionsList[i].arrow) {
                        scene.remove(connectionsList[i].arrow);
                        connectionsList[i]['arrow'] = null;
                    }
                    //删除线
                    if (connectionsList[i].line.parent) {
                        connectionsList[i].line.parent.remove(connectionsList[i].line);
                        connectionsList[i].line = null;
                    }
                    //删除开始点
                    if (connectionsList[i].sPoint) {
                        scene.remove(connectionsList[i].sPoint);
                        connectionsList[i].sPoint = null;
                    }
                    //删除结束点
                    if (connectionsList[i].ePoint) {
                        scene.remove(connectionsList[i].ePoint)
                        connectionsList[i].ePoint = null;
                    }
                    connectionsList.splice(i, 1);
                    selectedNode.value = null;
                    proxy.$modal.msgSuccess("删除成功");
                    break
                }
            }
            updateTopology();
        } else {
            proxy.$modal.msgError("请选择要删除的线");
        }
    } catch (e) {
        console.error("🚀 ~ delLine ~ e:", e);
    }
}


// 场景中点击事件处理
const handleCanvasClick = (event) => {
    const rect = renderer.domElement.getBoundingClientRect();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects([...nodeList.value.map(n => n.meshRef), ...connectionsList.map(n => n.line)])
    // 检测是否点击了连接线
    if (intersects.length > 0) {
        const clickedMesh = intersects[0].object
        //遍历模型
        let find = nodeList.value.find(n => n.meshRef === clickedMesh);
        if (!find) {
            //遍历线
            find = connectionsList.find(n => n.line === clickedMesh);
            lineEndId.value = find.endId ? find.endId : null;
            lineStartId.value = find.startId ? find.startId : null;
            handleToolbarClick(4);
            resetHighlight();
        } else {
            if (!lineDragFlag) {
                //点击模型 将工具栏切换为0
                console.log("aaa",bottomToolbarRef.value.bottomToolbarActive)
                if (bottomToolbarRef.value.bottomToolbarActive == 4) {
                    handleToolbarClick(0);
                    rightTabs.value = 1;
                }
                //  highlight(find.meshRef, find.id);
                delBoxHelper();
                boxHelper = new THREE.BoxHelper(find.meshRef, selectedColor);
                scene.add(boxHelper);

                requestAnimationFrame(() => {
                    renderer.render(scene, camera);
                });
            }

        }

        selectedNode.value = find;
    }
}

//更新拓扑图数据
const updateTopology = () => {
    topologyRef?.value?.setData(connectionsList);
}

// 原始材质备份
let originalMaterials = {};
const highlight = (mesh, id) => {
    if (mesh.userData.type == 'model') {
        for (const key in originalMaterials) {
            resetHighlight(key)
        }
        originalMaterials = {};
        // 备份原始材质
        originalMaterials[id] = [...mesh.material];
        // 创建高亮材质数组
        const highlightMaterials = originalMaterials[id].map(mat => {
            return mat.clone();
        });
        // 统一修改颜色
        highlightMaterials.forEach(mat => {
            mat.emissive.setHex(selectedColor);
            mat.emissiveIntensity = 16;
        });
        // 应用新材质
        mesh.material = highlightMaterials;
    }
}

const resetHighlight = (id) => {
    //有id恢复其他的材质
    if (id) {
        const mesh = nodeList.value.find(n => n.id == id);
        // 恢复原始材质
        if (mesh && mesh.meshRef) {
            mesh.meshRef.material = originalMaterials[id];
        }
    } else {
        //恢复所有材质
        for (const key in originalMaterials) {
            const mesh = nodeList.value.find(n => n.id == key);
            if (mesh && mesh.meshRef) {
                mesh.meshRef.material = originalMaterials[key];
            }
        }
        originalMaterials = {};
    }
}

// 初始化场景
const initScene = async () => {
    // 渲染器
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
    renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true })
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setClearColor(0x12345A)
    canvasContainer.value.appendChild(renderer.domElement)
    camera.position.set(8, 12, 8)
    camera.lookAt(0, 0, 0)
    scene.background = new THREE.Color(0x12345A); // 背景色
    scene.fog = new THREE.Fog(0x12345A, 0, 80); // 雾效颜色与背景一致
    // 光源
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
    scene.add(ambientLight)
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
    directionalLight.position.set(4, 10, 6)
    scene.add(directionalLight)
    // 控制器
    controls = new OrbitControls(camera, renderer.domElement)

    //  controls.enabled = false
    // controls.enableDamping = true
    // controls.dampingFactor = 0.02 // 阻尼系数（默认0.05）
    //  controls.autoRotate = false;
    // controls.enablePan = false;    // 禁止平移
    controls.enableRotate = false; // 禁止旋转
    //  controls.screenSpacePanning = true; // 默认值，沿屏幕平面平移
    controls.enableZoom = false;   // 禁止缩放
    if (type.value == 0) {
        controls.enableRotate = false; // 禁止旋转
        controls.enableZoom = false;   // 禁止缩放
    } else {
        controls.enableRotate = true; // 禁止旋转
        controls.enableZoom = true;   // 禁止缩放

    }
    controls.minDistance = 10;      // 相机最小距离
    controls.maxDistance = 30;     // 相机最大距离
    controls.minPolarAngle = 1;   // 垂直旋转下限（弧度）
    controls.maxPolarAngle = 1.3; // 垂直旋转上限

    // 地面网格
    const gridHelper = new THREE.GridHelper(100, 30)
    gridHelper.material.transparent = true;  // 启用透明
    gridHelper.material.opacity = 0.3;      // 设置透明度
    // gridHelper.position.y = -4
    //  gridHelper.position.z = -10
    gridHelper.rotation.x = 0
    scene.add(gridHelper)

    //创建指示器
    createAxis();
    await getData();
    await initModels()
    if (id) {
        initSceneData();
    }
    animate()
}


/**
 * 创建坐标指示器
 */
const createAxis = () => {
    function createAxis(length, color, direction) {
        const points = [
            new THREE.Vector3(0, 0, 0),
            new THREE.Vector3().copy(direction).multiplyScalar(length)
        ];
        const geometry = new THREE.BufferGeometry().setFromPoints(points);
        const material = new THREE.LineBasicMaterial({ color: color });
        return new THREE.Line(geometry, material);
    }
    // 创建三轴
    const xAxis = createAxis(100, 0xff7500, new THREE.Vector3(1, 0.001, 0));
    const yAxis = createAxis(100, 0x00FF00, new THREE.Vector3(0, 1.001, 0));
    const zAxis = createAxis(100, 0xff00ff, new THREE.Vector3(0, 0.002, 1));
    scene.add(xAxis, yAxis, zAxis);
}


//加载模型
const initModels = async () => {
    try {
        proxy.$modal.loading('资源加载中')
        //加载模型
        const loader = new GLTFLoader();
        const modelUrls = equipmentList.value.map(n => `${import.meta.env.VITE_APP_BASE_URL}${n.files?.[0].url}`);
        const loadPromises = modelUrls.map(url => loader.loadAsync(url));
        await Promise.all(loadPromises).then(res => {
            res.forEach((gltf, index) => {
                gltf.scene.traverse(function (obj) {
                    if (obj.isMesh) {
                        obj.material.emissive = obj.material.color;
                        obj.material.emissiveMap = obj.material.map;
                    }
                });
                // ===== 1. 预处理几何体 =====
                function preprocessGeometry(geometry, matrixWorld) {
                    const clonedGeo = geometry.clone();
                    // 应用世界变换矩阵（解决合并后位置错乱）
                    clonedGeo.applyMatrix4(matrixWorld); // 网页1/
                    // 强制补全必要属性（网页3/网页5）
                    ['uv', 'normal', 'uv1'].forEach(attr => {
                        if (!clonedGeo.attributes[attr]) {
                            const itemSize = attr === 'normal' ? 3 : 2;
                            const array = new Float32Array(clonedGeo.attributes.position.count * itemSize);
                            clonedGeo.setAttribute(attr, new THREE.BufferAttribute(array, itemSize));
                        }
                    });
                    return clonedGeo;
                }
                // ===== 2. 收集与合并几何体 =====
                const geometries = [];
                gltf.scene.traverse(obj => {
                    if (obj.isMesh) {
                        const processedGeo = preprocessGeometry(obj.geometry, obj.matrixWorld);
                        geometries.push(processedGeo);
                    }
                });
                // 合并几何体（支持多材质分组）
                const mergedGeometry = mergeGeometries(geometries, {
                    useGroups: true,   // 网页4
                    keepIndices: false // 优化顶点索引
                });
                // 顶点合并优化（容差0.01米）
                const optimizedGeometry = mergeVertices(mergedGeometry, 0.01); // 网页1/网页2
                // 重新计算法线（网页5）
                optimizedGeometry.computeVertexNormals();
                // ===== 4. 创建合并网格 =====
                const materials = [];
                gltf.scene.traverse(obj => {
                    if (obj.isMesh) materials.push(obj.material);
                });
                const mergedMesh = new THREE.Mesh(optimizedGeometry, materials);
                // ===== 5. 内存清理 =====
                geometries.forEach(geo => geo.dispose()); // 网页2
                // 将几何体顶点居中到原点
                mergedMesh.traverse((child) => {
                    if (child.isMesh) {
                        child.geometry.center();
                    }
                })
                models[equipmentList.value[index].id] = mergedMesh;
            });
        });
        proxy.$modal.closeLoading();
    } catch (error) {
        console.log("🚀 ~ initModels ~ error:", error)
        proxy.$modal.msgError('模型加载失败');
    }
}



//模型前后节点连接判断 线的颜色设置正常绿色 红色异常 错误提示
const connectionRule = (index) => {
    //线两端的模型
    const sNode = nodeList.value.find(n => n.id == connectionsList[index].startId);
    const eNode = nodeList.value.find(n => n.id == connectionsList[index].endId);
    if (sNode && eNode) {
        //sNode节点的 后节点的设备类型是否包含 eNode节点的设备类型
        let sFind = sNode.afterNode.find(i => i.nodeEquipmentType == eNode.typeId);
        //eNode节点 的前几点设备类型是否包含 sNode节点的设备类型
        let eFind = eNode.firstNode.find(i => i.nodeEquipmentType == sNode.typeId);
        if (sFind && eFind) {
            //遍历所有连接线 限制每个设备能连接的类型数量
            let sNum = 0; //sNode 开始点 配置的后节点连接设备的类型数量
            let eNum = 0; //eNode 结束点 配置的前节点连接设备的类型数量
            connectionsList.forEach(item => {
                //连接线的开始节点等于当前的前节点
                if (item.startId == sNode.id) {
                    //查找对应的后节点
                    let end = nodeList.value.find(n => n.id == item.endId);
                    //后节点的类型等于 等于当前前节点设备类型
                    if (end && end.typeId == sFind.nodeEquipmentType) {
                        sNum++;
                    }
                }
                //连接线的开始节点等于当前的前节点
                if (item.endId == eNode.id) {
                    //查找对应的后节点
                    let start = nodeList.value.find(n => n.id == item.startId);
                    //后节点的类型等于 等于当前前节点设备类型
                    if (start && start.typeId == eFind.nodeEquipmentType) {
                        eNum++;
                    }
                }
            })
            if (sNum <= parseInt(sFind.nodeNum) && eNum <= parseInt(eFind.nodeNum)) {
                //判断前后
                if (sNode.meshRef.position.x < eNode.meshRef.position.x) {
                    connectionsList[index].status = 0;
                    connectionsList[index].line.material.color.set(lineColor);
                } else {
                    connectionsList[index].line.material.color.set(lineErrorColor);
                    connectionsList[index]['errorMsg'] = `${sNode.label}与${eNode.label}连接的设备前后位置错误`;
                    if (connectionsList[index].status == 0) {
                        proxy.$modal.msgError(connectionsList[index]['errorMsg']);
                    }
                    connectionsList[index].status = 1;
                }
            } else {
                //设置线的状态
                connectionsList[index].status = 1;
                connectionsList[index].line.material.color.set(lineErrorColor);
                connectionsList[index]['errorMsg'] = `${sNode.label}与${eNode.label}连接的设备数量超出限制`;
                proxy.$modal.msgError(connectionsList[index]['errorMsg']);
            }
        } else {
            //判断反方向
            let sFind = sNode.firstNode.find(i => i.nodeEquipmentType == eNode.typeId);
            let eFind = eNode.afterNode.find(i => i.nodeEquipmentType == sNode.typeId);
            if (sFind && eFind) {
                //反方向判断   前后位置
                if (sNode.meshRef.position.x > eNode.meshRef.position.x) {
                    connectionsList[index].status = 0;
                    connectionsList[index].line.material.color.set(lineColor);
                } else {
                    connectionsList[index].line.material.color.set(lineErrorColor);
                    connectionsList[index]['errorMsg'] = `${sNode.label}与${eNode.label}连接的设备前后位置错误`;
                    if (connectionsList[index].status == 0) {
                        proxy.$modal.msgError(connectionsList[index]['errorMsg']);
                    }
                    connectionsList[index].status = 1;
                }
            } else {
                connectionsList[index].line.material.color.set(lineErrorColor);
                connectionsList[index]['errorMsg'] = `${sNode.label}与${eNode.label}连接逻辑错误`;
                if (connectionsList[index].status == 0) {
                    proxy.$modal.msgError(connectionsList[index]['errorMsg']);
                }
                connectionsList[index].status = 1;
            }
        }
    }
}

/**
 * 设置点的位置为模型位置
 */
const setPointModel = (point, model, event) => {
    model.meshRef.updateMatrixWorld(true);
    // let sModel = nodeList.value.find(n => n.id == n.startId);
    // model.meshRef.updateMatrixWorld(true);
    point.updateMatrixWorld(true);

    // 计算目标物体的几何中心（本地坐标系）
    model.meshRef.geometry.computeBoundingBox(); // 必须先计算包围盒
    const center = new THREE.Vector3();
    model.meshRef.geometry.boundingBox.getCenter(center); // 获取几何中心

    // 将本地中心转换为世界坐标
    const worldCenter = new THREE.Vector3();
    model.meshRef.localToWorld(worldCenter.copy(center));
    // 更新圆点位置（初始同步）
    point.position.copy(worldCenter);
    updateConnections(event);
    initDragControls();
    proxy.$modal.msgSuccess('连接到' + model.label);
    // 优化渲染触发
    requestAnimationFrame(() => {
        renderer.render(scene, camera);
    });
}

// 初始化拖拽控制
const initDragControls = () => {
    if (dragControls) {
        dragControls.dispose();
    }
    let draggableMeshes = nodeList.value.map(n => n.meshRef).filter(Boolean);
    console.log("🚀 ~ initDragControls ~  draggableMeshes.value :", draggableMeshes)

    const lines = [];
    connectionsList.forEach(n => {
        if (n.startId || n.endId) {

        } else {
            lines.push(n.line)
        }
    })
    draggableMeshes = [...draggableMeshes, ...lines]
    draggableMeshes = [...draggableMeshes, ...pointList.map(n => n.point).filter(Boolean)]
    dragControls = new DragControls(draggableMeshes, camera, renderer.domElement)
    dragControls.addEventListener('dragstart', event => {
        controls.enabled = false
    })
    //拖拽结束
    dragControls.addEventListener('dragend', event => {
        controls.enabled = true;
        lineDragFlag = false;
        if (event.object.userData.type == 'point') {
            for (const model of nodeList.value) {
                if (model.type != 'line') {
                    //获取场景中模型
                    let point = event.object;
                    //获取 点到物体的距离
                    const lineBox = new THREE.Box3().setFromObject(point);
                    const targetBox = new THREE.Box3().setFromObject(model.meshRef);
                    //获取当前拖拽的点
                    let pointF = pointList.find(n => n.id == point.name);
                    //获取点对应的线
                    let curline = connectionsList.find(n => n.id == pointF.lineId);
                    //当前线开始物体和结束物体都有 不往下执行 
                    if (curline.startId && curline.endId) {
                        return
                    } else if ((curline.endId && curline.endId == model.name) || (curline.startId && curline.startId == model.name)) {
                        //当前线结束点有连接物体并且等于物体 结束本次循环
                    }
                    //计算点与物体的距离小于3
                    const distance = distanceBetweenBoxes(lineBox, targetBox);
                    if (distance < 2) {
                        for (let index = 0; index < connectionsList.length; index++) {
                            if (connectionsList[index].id == pointF.lineId) {
                                if (connectionsList[index].startId && connectionsList[index].endId) {
                                    continue
                                } else if (connectionsList[index].startId) {
                                    //排除线离得近的物体已经存在连接线 
                                    let bool = connectionsList.some(n => (n.startId == connectionsList[index].startId && n.endId == model.id))
                                    if (bool) {
                                        continue
                                    }
                                    //连接结束点
                                    connectionsList[index].endId = model.id;
                                    //更新圆点的位置
                                    setPointModel(point, model, event)
                                    //连接逻辑
                                    connectionRule(index);
                                    updateTopology();
                                    return
                                } else if (connectionsList[index].endId) {
                                    ////排除线离得近的物体已经存在连接线 
                                    let bool = connectionsList.some(n => (n.endId == connectionsList[index].endId && n.startId == model.id))
                                    if (bool) {
                                        continue
                                    }
                                    //连接开始点
                                    connectionsList[index].startId = model.id;
                                    //更新圆点的位置
                                    setPointModel(point, model, event)
                                    //连接逻辑
                                    connectionRule(index);
                                    updateTopology();
                                    return
                                } else {
                                    //1 开始 2结束
                                    if (pointF.direction == 1) {
                                        connectionsList[index].startId = model.id;
                                    } else {
                                        connectionsList[index].endId = model.id;
                                    }
                                    setPointModel(point, model, event)
                                    return
                                }
                            }
                        }

                        break;
                    }

                }
            }

        }
    })

    dragControls.addEventListener('drag', throttle(function (event) {

        updateConnections(event, false);
    }, 50));


}


/**
 * 线的两端
 * @param position 控制点位置
 */
const addControlPoint = (position) => {
    const sphereGeometry = new THREE.SphereGeometry(0.06);
    const sphereMaterial = new THREE.MeshBasicMaterial({ color: lineColor });
    const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.position.copy(position);
    scene.add(sphere);
    return sphere;
}

//计算两个物体距离
const distanceBetweenBoxes = (boxA, boxB) => {
    // 沿每个轴计算间隔
    const x = Math.max(0, Math.max(boxA.min.x - boxB.max.x, boxB.min.x - boxA.max.x));
    const y = Math.max(0, Math.max(boxA.min.y - boxB.max.y, boxB.min.y - boxA.max.y));
    const z = Math.max(0, Math.max(boxA.min.z - boxB.max.z, boxB.min.z - boxA.max.z));
    return Math.sqrt(x * x + y * y + z * z);
}


// 创建节点
const createNode = async (nodeConfig, position) => {
    //加载模型
    let glb = models[nodeConfig.id].clone();
    //设置模型缩放大小
    if (nodeConfig.size) {
        const scale = nodeConfig.size.split(',').map(i => parseFloat(i));
        glb.scale.set(...scale);
    }
    //旋转角度
    if (nodeConfig.rotation) {
        const rotation = nodeConfig.rotation.split(',').map(i => parseFloat(i));
        glb.rotation.set(...rotation);
    }
    const bbox = new THREE.Box3().setFromObject(glb);
    const center = new THREE.Vector3();
    bbox.getCenter(center)
    // let size = bbox.getSize(new THREE.Vector3());
    glb.position.sub(center); // 将模型中心点对齐到原点
    glb.position.add(position); // 设置到目标位置
    glb.userData['type'] = 'model';
    glb.userData['id'] = generateUniqueId();
    scene.add(glb);

    //  boxHelper = new THREE.BoxHelper(glb, 0xff461f);
    // scene.add(boxHelper);
    // // 3. 创建临时原点标记（验证坐标中心）
    // const originMarker = new THREE.Mesh(
    //     new THREE.SphereGeometry(0.1),
    //     new THREE.MeshBasicMaterial({ color: 0x00ff00 })
    // );
    // glb.add(originMarker); // 应出现在模型底部中心
    // 5. 初始化调用
    createAccurateLabel(glb, nodeConfig.equipmentName, glb.userData['id']);

    const newNode = {
        id: glb.userData['id'],//唯一标识
        nodeId: nodeConfig.id, //设备id
        label: nodeConfig.equipmentName,//设备名称
        manufactor: nodeConfig.manufactor,//厂家
        model: nodeConfig.model,//型号
        meshRef: glb,//模型实例
        afterNode: nodeConfig.afterNode,//下节点
        firstNode: nodeConfig.firstNode,//上节点
        typeId: nodeConfig.typeId, //设备类型id
        remark: nodeConfig.remark,//作用
    }
    nodeList.value.push(newNode)
    initDragControls();

}
//删除包装盒子
const delBoxHelper = () => {
    if (boxHelper) {
        if (boxHelper.parent) {
            boxHelper.parent.remove(boxHelper);
        }

        // 2. 释放 GPU 资源
        boxHelper.geometry.dispose();  // 销毁几何体
        boxHelper.material.dispose();  // 销毁材质

        // 3. 清除 JavaScript 引用
        boxHelper = null;
    }

}
// 4. 修正标签定位
function createAccurateLabel(model, text, id) {
    // 重新计算包围盒（确保模型矩阵已更新）
    model.updateMatrixWorld(true);
    const box = new THREE.Box3().setFromObject(model);
    const size = box.getSize(new THREE.Vector3());
    // 动态计算偏移量（适配不同单位制）
    const unitScale = size.y > 10 ? 0.01 : 1; // 自动检测厘米/米单位
    const offsetY = (size.y / 2) * unitScale + 0.5;
    // 创建定位锚点（三维空间标记）
    const labelAnchor = new THREE.Object3D();
    labelAnchor.position.set(0, offsetY, 0);
    model.add(labelAnchor);
    // 创建带透视修正的标签
    const labelDiv = document.createElement('div');
    labelDiv.textContent = text;
    labelDiv.style.cssText = `
    transform: translateX(-50%) scale(${1 / camera.zoom});
    transition: transform 0.1s;
    filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
  `;
    // 绑定到定位锚点
    const label = new CSS2DObject(labelDiv);
    labelAnchor.add(label);
    // 3. 将标签附加到模型上
    model.add(label); // model 是你的 3D 模型对象
    // 4. 初始化 CSS2D 渲染器
    let labelRenderer = new CSS2DRenderer();
    labelRenderer.setSize(window.innerWidth, window.innerHeight);
    labelRenderer.domElement.style.position = 'absolute';
    labelRenderer.domElement.style.color = '#fff'
    labelRenderer.domElement.style.top = '0';
    labelRenderer.domElement.style.pointerEvents = 'none'; // 关键：整个标签层不拦截事件
    document.body.appendChild(labelRenderer.domElement);
    labelRendererObj[id] = labelRenderer;
}



//生成唯一id
const generateUniqueId = () => {
    // 时间戳（13位） + 4位随机数
    const timestamp = Date.now().toString(16); // 转为16进制缩短长度
    const random = Math.floor(Math.random() * 0x10000).toString(16).padStart(4, '0');
    return `${timestamp}-${random}`;
}

// 新增几何中心缓存（使用WeakMap避免内存泄漏）
const geometryCenterCache = new WeakMap();

// 获取带缓存的几何中心（模型坐标系）
const getModelSpaceCenter = (mesh) => {
    if (!geometryCenterCache.has(mesh.geometry)) {
        mesh.geometry.computeBoundingBox();
        const center = new THREE.Vector3();
        mesh.geometry.boundingBox.getCenter(center);
        geometryCenterCache.set(mesh.geometry, center);
    }
    return geometryCenterCache.get(mesh.geometry).clone();
};

//拖拽线和点的时候标识 禁止拖拽的时候可以选中物体判断
let lineDragFlag = false;
// 更新连线
let updating = false;
//isAll 是否更新全部
const updateConnections = (e, isAll = true) => {
    if (updating || connectionsList.length == 0) return
    updating = true
    if (e && e.object) {
        //物体
        if (e.object.userData && e.object.userData.type == "model") {
            if (!isAll) {
                //判断节点是否有关联 无关联的不更新位置 减少不必要的性能消耗
                let connectEmpty = connectionsList.some(l => l.startId == e.object.userData.id || l.endId == e.object.userData.id)
                if (!connectEmpty) {
                    updating = false;
                    return;
                }
            }
            for (let index = 0; index < connectionsList.length; index++) {
                let s = nodeList.value.find(n => n.id == connectionsList[index].startId);
                let ee = nodeList.value.find(n => n.id == connectionsList[index].endId);
                if (connectionsList[index].startId && connectionsList[index].endId) {
                    // 获取缓存几何中心（模型坐标系）
                    const modelStart = getModelSpaceCenter(s.meshRef);
                    const modelEnd = getModelSpaceCenter(ee.meshRef);

                    // 转换为世界坐标
                    const worldStart = new THREE.Vector3();
                    const worldEnd = new THREE.Vector3();
                    s.meshRef.localToWorld(worldStart.copy(modelStart));
                    ee.meshRef.localToWorld(worldEnd.copy(modelEnd))
                    connectionsList[index].sPoint.position.copy(worldStart);
                    connectionsList[index].ePoint.position.copy(worldEnd);
                    // 更新圆点位置（初始同步）      
                    connectionsList[index].sPoint.updateMatrixWorld(true);
                    connectionsList[index].ePoint.updateMatrixWorld(true);
                    connectionsList[index].line.position.set(0, 0, 0); // 重置物体位置
                    connectionsList[index].line.geometry.setFromPoints([connectionsList[index].sPoint.position, connectionsList[index].ePoint.position])
                    connectionsList[index].line.geometry.attributes.position.needsUpdate = true
                    connectionsList[index].line.geometry.computeBoundingSphere();
                    connectionRule(index);
                    // break;
                } else if (connectionsList[index].startId) {
                    const modelStart = getModelSpaceCenter(s.meshRef);
                    // 转换为世界坐标
                    const worldStart = new THREE.Vector3();
                    s.meshRef.localToWorld(worldStart.copy(modelStart));
                    connectionsList[index].sPoint.position.copy(worldStart);
                    connectionsList[index].sPoint.updateMatrixWorld(true);
                    connectionsList[index].ePoint.updateMatrixWorld(true);
                    connectionsList[index].line.position.set(0, 0, 0); // 重置物体位置
                    connectionsList[index].line.geometry.setFromPoints([connectionsList[index].sPoint.position, connectionsList[index].ePoint.position])
                    connectionsList[index].line.geometry.attributes.position.needsUpdate = true;
                    connectionsList[index].line.geometry.computeBoundingSphere();
                    connectionRule(index);
                    // break;
                } else if (connectionsList[index].endId) {

                    const modelEnd = getModelSpaceCenter(ee.meshRef);
                    // 转换为世界坐标
                    const worldEnd = new THREE.Vector3();
                    ee.meshRef.localToWorld(worldEnd.copy(modelEnd));
                    connectionsList[index].sPoint.position.copy(worldEnd);
                    connectionsList[index].sPoint.updateMatrixWorld(true);
                    connectionsList[index].ePoint.updateMatrixWorld(true);
                    connectionsList[index].line.position.set(0, 0, 0); // 重置物体位置
                    connectionsList[index].line.geometry.setFromPoints([connectionsList[index].sPoint.position, connectionsList[index].ePoint.position])
                    connectionsList[index].line.geometry.attributes.position.needsUpdate = true
                    connectionsList[index].line.geometry.computeBoundingSphere();
                    connectionRule(index);
                    //break
                } else {

                }
            }
        }
        //线
        if (e.object.type == "Line") {
            lineDragFlag = true;
            connectionsList.forEach(i => {
                i.line.geometry.attributes.position.needsUpdate = true;
                const vertices = i.line.geometry.attributes.position.array;
                //更新线的两端圆点位置
                if (i.sPoint && i.ePoint) {
                    i.line.updateMatrixWorld(true);
                    // 获取顶点本地坐标
                    const localStart = new THREE.Vector3().fromBufferAttribute(
                        i.line.geometry.attributes.position, 0
                    );
                    const localEnd = new THREE.Vector3().fromBufferAttribute(
                        i.line.geometry.attributes.position, 1
                    );
                    // 转换为世界坐标
                    const worldStart = localStart.applyMatrix4(i.line.matrixWorld);
                    const worldEnd = localEnd.applyMatrix4(i.line.matrixWorld);
                    // 更新球体位置
                    i.sPoint.position.copy(worldStart);
                    i.ePoint.position.copy(worldEnd);
                }
            })
        }
        //两端的点
        if (e.object.userData.type == "point") {
            lineDragFlag = true;
            let lineId = pointList.find(p => p.id == e.object.name).lineId;
            let lineObj = connectionsList.find(c => c.id == lineId);
            lineObj.sPoint.updateMatrixWorld(true);
            lineObj.ePoint.updateMatrixWorld(true);
            lineObj.line.position.set(0, 0, 0); // 重置物体位置
            lineObj.line.geometry.setFromPoints([lineObj.sPoint.position, lineObj.ePoint.position])
            lineObj.line.geometry.attributes.position.needsUpdate = true
            lineObj.line.geometry.computeBoundingSphere();
        }
        // 优化渲染触发
        requestAnimationFrame(() => {
            renderer.render(scene, camera);
        });
    }
    updating = false
    // initDragControls()

}


// 处理工具栏拖拽
const handleToolbarDragStart = (node, event) => {
    currentDraggingNode = node
    if (node.type == 'line1') {
        //选中线
        bottomToolbarRef.value.handleToolbarClick(4);
        //选中配置
        rightTabs.value = 0;
    }
    event.dataTransfer.setData('text/plain', '')
}

//拖拽结束
const handleToolbarDragEnd = (event) => {
    if (!currentDraggingNode) return
    const rect = renderer.domElement.getBoundingClientRect()
    if (
        event.clientX < rect.left ||
        event.clientX > rect.right ||
        event.clientY < rect.top ||
        event.clientY > rect.bottom
    ) {
        currentDraggingNode = null
        return
    }
    const mouse = new THREE.Vector2();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
    const raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(scene.children);
    if (intersects.length > 0) {
        if (currentDraggingNode.type == 'line1') {
            createLine(currentDraggingNode, intersects[0].point);
        } else {
            createNode(currentDraggingNode, intersects[0].point);
        }

    }
    currentDraggingNode = null;
}


const updateArrow = () => {
    connectionsList.forEach(i => {
        if (i.startId && i.endId) {
            if (i.status == 0) {


                if (i.arrow) {
                    const positions = i.line.geometry.attributes.position.array;
                    const start = new THREE.Vector3(positions[0], positions[1], positions[2]); // 起点
                    const end = new THREE.Vector3(positions[3], positions[4], positions[5]);   // 终点
                    const midpoint = new THREE.Vector3()
                        .addVectors(start, end)
                        .multiplyScalar(0.5);
                    i.t += i.speed;
                    if (i.t > 1) i.t = 0; // 循环播放
                    const currentPosition = new THREE.Vector3().lerpVectors(start, end, i.t);

                    // 更新方向：始终指向线段方向
                    const direction = new THREE.Vector3().subVectors(end, start).normalize();

                    // 更新箭头
                    i.arrow.position.copy(currentPosition);
                    i.arrow.setDirection(direction);
                } else {
                    const positions = i.line.geometry.attributes.position.array;
                    const start = new THREE.Vector3(positions[0], positions[1], positions[2]); // 起点
                    const end = new THREE.Vector3(positions[3], positions[4], positions[5]);   // 终点
                    const midpoint = new THREE.Vector3()
                        .addVectors(start, end)
                        .multiplyScalar(0.5);

                    // 2. 计算线段方向向量（从起点到终点）
                    const direction = new THREE.Vector3()
                        .subVectors(end, start)
                        .normalize(); // 单位化方向向量

                    // 创建箭头，方向与线段一致
                    const arrow = new THREE.ArrowHelper(
                        direction.clone().normalize(), // 方向向量（单位化）
                        midpoint,                         // 起点与线段一致
                        1.2,            // 箭头长度 = 线段实际长度
                        0xff461f,
                        0.6,               // 颜色（红色）
                        0.2
                        // 0.2 * direction.length(),      // 头部长度（按比例）
                        // 0.1 * direction.length()       // 头部宽度（按比例）
                    );
                    i['arrow'] = arrow;
                    i['speed'] = 0.003;
                    i['t'] = 0;
                    scene.add(arrow);
                }
            } else {
                if (i.arrow) {
                    scene.remove(i.arrow)
                    i['arrow'] = null;
                }
            }
        } else {
            //判断是否是线段如果没有连接两个节点则删除箭头
            if (i.arrow) {
                scene.remove(i.arrow)
                i['arrow'] = null;
            }
        }

    })
}

// 窗口大小调整
const onWindowResize = () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
    renderer.setSize(window.innerWidth, window.innerHeight)
}

// 生命周期
onMounted(() => {

    type.value = parseInt(router.currentRoute.value.query.type);

    initScene();
    // 添加点击事件监听
    renderer.domElement.addEventListener('click', handleCanvasClick)
    window.addEventListener('resize', onWindowResize)
})

onBeforeUnmount(() => {
    window.removeEventListener('resize', onWindowResize)
    // 清理节点
    nodeList.value.forEach(node => scene.remove(node.meshRef))
    connectionsList.forEach(node => scene.remove(node.line))
    renderer.dispose()
    canvasContainer.value?.removeChild(renderer.domElement)
    // 移除点击事件监听
    renderer.domElement.removeEventListener('click', handleCanvasClick)
    for (const key in labelRendererObj) {
        const rendererDom = labelRendererObj[key].domElement;
        if (document.body.contains(rendererDom)) {
            document.body.removeChild(rendererDom);
        }
    }
    delBoxHelper();
})
// 动画循环
const animate = () => {
    requestAnimationFrame(animate)
    controls.update();
    updateArrow();
    for (const key in labelRendererObj) {
        labelRendererObj[key].render(scene, camera); // 同时渲染标签   
    }
    boxHelper?.update();
    renderer.render(scene, camera)

}
</script>

<style scoped lang="scss">
.container {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
.coord {
    position: absolute;
    top: 100px;
    right: 500px;
    color: #fff;
    z-index: 100;
    width: 170px;
    height: 144px;
    user-drag: none; /* 标准属性 */
    -webkit-user-drag: none; /* Safari兼容 */
}
.canvas-container {
    width: 100%;
    height: 100%;
    touch-action: none;
    background: #12345a;
}
.divider {
    width: 100%;
    height: 1px;
    background: #516473;
}
.bottom {
    position: absolute;
    bottom: 18px;
    left: calc(50% - 240px);
}

.canvas-container {
    width: 100%;
    height: 100%;
    touch-action: none;
    background: #12345a;
}
.divider {
    width: 100%;
    height: 1px;
    background: #516473;
}
.bottom {
    position: absolute;
    bottom: 18px;
    left: calc(50% - 240px);
    z-index: 1;
    width: 480px;
    height: 60px;
    border-radius: 14px;
    background: rgba(0, 17, 46, 0.3);
    backdrop-filter: blur(8px);
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding: 0 30px;
    &-item {
        width: 38px;
        height: 38px;
        display: flex;
        align-items: center;
        justify-content: center;
        img {
            width: 22px;
            height: 22px;
        }
        &:hover {
            border-radius: 6px;
            background: #23446f;
        }
        &:active {
            border-radius: 6px;
            background: #3670f7;
        }
    }
    &-actvie {
        width: 38px;
        height: 38px;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 6px;
        background: #3670f7;
        img {
            width: 22px;
            height: 22px;
        }
    }
}

.right-pack {
    position: absolute;
    top: 26px;
    right: 30px;

    padding: 4px 6px;
    z-index: 100;
    cursor: pointer;
    border-radius: 4px;
    background: #23446f;
    &:active {
        opacity: 0.8;
    }
    // &:hover {
    //     border-radius: 4px;
    //     background: #23446f;
    // }

    img {
        width: 18px;
        height: 18px;
    }
}

@keyframes slideInRight {
    from {
        transform: translateX(100%);
    }
    to {
        transform: translateX(0);
    }
}

@keyframes slideOutRight {
    from {
        transform: translateX(0);
    }
    to {
        transform: translateX(100%);
    }
}

.slide-in-right {
    animation: slideInRight 0.5s forwards;
}

.slide-out-right {
    animation: slideOutRight 0.5s forwards;
}

.right {
    position: absolute;
    top: 18px;
    right: 18px;
    z-index: 100;
    // height: 100px;
    width: 300px;
    border-radius: 10px;
    background: rgba(0, 17, 46, 0.3);
    backdrop-filter: blur(8px);
    &-info {
        padding: 10px 16px;
        &-name {
            margin-top: 10px;
            color: #fff;
            font-size: 14px;
        }
        &-item {
            min-height: 36px;
            display: flex;
            border-radius: 4px;
            background: #13325a;
            align-items: center;
            color: #fff;
            font-size: 14px;
            margin: 10px 0;
            padding: 4px 10px;

            &-label {
                width: 60px;
            }
            &-value {
                display: flex;
                flex-wrap: wrap;
                gap: 4px;
                width: 190px;
            }
        }
    }
    &-config {
        padding: 20px 16px;
        &-line {
            &-error {
                margin-bottom: 10px;
                font-size: 14px;
                color: #ff4d4f;
            }
            &-node {
                display: flex;
                margin-bottom: 10px;
                &-label {
                    color: #fff;
                    font-style: 14px;
                    width: 80px;
                }
                /* 输入框高度/边框/圆角 */
                :deep(.el-select) {
                    border: 1px solid #4893ee;
                    border-radius: 4px;
                    .el-select__wrapper {
                        box-shadow: none;
                        background: transparent;
                        .el-select__selected-item {
                            color: #fff;
                        }
                    }
                }
            }
        }
        &-scale {
            &-label {
                color: #fff;
                font-size: 14px;
                margin-bottom: 16px;
            }
            &-btn {
                margin-top: 20px;
                height: 30px;
                width: 100%;
            }
        }
        &-rotation {
            &-item {
                display: flex;
                align-items: center;
                height: 34px;
                &-label {
                    margin-right: 10px;
                    color: #fff;
                    font-size: 14px;
                }
            }
            &-btn {
                margin-top: 20px;
                height: 30px;
                width: 100%;
            }
        }
        &-del {
            height: 30px;
            width: 100%;
        }
    }
    &-tabs {
        height: 50px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        width: 180px;
        padding-left: 20px;

        &-item {
            cursor: pointer;
            color: #bcbcbc;
            font-size: 16px;
        }
        &-active {
            cursor: pointer;
            color: #fff;
            font-size: 16px;
        }
    }
}
</style>